package monitor

import (
	"fmt"
	"context"
	"net/url"

	"github.com/prometheus/client_golang/api"
	"github.com/prometheus/client_golang/api/prometheus/v1"
	"time"
	"github.com/prometheus/common/model"
	"strconv"
	"hulk-api/conf"
	"log"
	"strings"
)

type Host string   //ipaddr

var prometheusServer string

func init()  {
	prometheusServer,_  =  conf.Cfg.GetValue("prometheus","prometheus_server")
}

func GetNodes() (nodes []string,err error)  {
	//https://ctolib.com/article/comments/110960
	cfg := api.Config{
		Address: prometheusServer,
	}
	proCli,err := api.NewClient(cfg)
	if err != nil {
		log.Println("connect to err is" , err.Error())
		return
	}
	proApi := v1.NewAPI(proCli)
	ctx := context.Background()
	//ctx, _ := context.WithCancel(context.Background())  //为毛一定要这个东西ctx
	query := "node_load1"

	res,err := proApi.Query(ctx,query,time.Time{})
	if err != nil {
		log.Println(err)
		return
	}
	m,ok := res.(model.Vector)
	if ok && m.Len() > 0 {
		temap := make(map[string]struct{})
		for _,j := range m {
			k := j.Metric["instance"]
			if _, ok := temap[string(k)]; ok {
				continue
			} else {
				nodes = append(nodes,strings.Split(string(k),":")[0])
				temap[string(k)] = struct{}{}
			}
			}
	}

	return
}

//服务器负载
func (h Host) SysLoad() (load []float64,err error) {
	//https://ctolib.com/article/comments/110960
	cfg := api.Config{
		Address: prometheusServer,
	}
	proCli,err := api.NewClient(cfg)
	if err != nil {
		log.Println("connect to err is" , err.Error())
		return
	}
	proApi := v1.NewAPI(proCli)
	ctx := context.Background()
	//ctx, _ := context.WithCancel(context.Background())  //为毛一定要这个东西ctx
	n := []int{1,5,15}
	//r := []float64{}
	for _,d := range n {
		q := fmt.Sprintf("node_load%d{instance=~'%s.*'}",d,string(h))
		//res,errr := proApi.Query(ctx,`node_load1{job="node1"}`,time.Now())  //又不会放入到goruntine中，此处的ctx有何用意？
		res,errr := proApi.Query(ctx,q,time.Time{})  //又不会放入到goruntine中，此处的ctx有何用意？

		if errr != nil {
			fmt.Println("query error is",errr.Error())
			return
		}
		m,ok := res.(model.Vector)
		if ok && m.Len() > 0 {
			//fmt.Println("m is" , m[0].Value)
			load = append(load,float64(m[0].Value))
		}
	}

	return
}

//内存使用率

type Memory struct {
	Total float64
	Used float64
	Precent string
}

func (m Memory) usage() string  {
	return fmt.Sprintf("%.2f",(1 - m.Used/m.Total) * 100)
}
func (h Host) MemoryUsage() (memory Memory,err error) {
	cfg := api.Config{
		Address: prometheusServer,
	}
	proCli,err := api.NewClient(cfg)
	if err != nil {
		fmt.Println("connect to err is" , err.Error())
		return
	}
	proApi := v1.NewAPI(proCli)
	ctx := context.Background()
	queryList := []string{"node_memory_MemTotal_bytes","node_memory_MemAvailable_bytes"}
	d := []float64{}
	for _,query :=range queryList {
		q := fmt.Sprintf("%s{instance=~'%s.*'}",query,string(h))
		res,errr := proApi.Query(ctx,q,time.Time{})
		if errr != nil {
			fmt.Println("query error is",errr.Error())
			return
		}
		m,ok := res.(model.Vector)

		if ok && m.Len() > 0 {
			d = append(d,float64(m[0].Value))
		}
	}

	if len(d) == 2 {
		memory.Total = d[0]
		memory.Used = d[1]
		memory.Precent = memory.usage()
	}
	return
}


type Disk struct {
	Device string
	Fstype string
	Mountpoint string

	Total float64
	Avail float64
	Availed string
	Precent string
}

func (d Disk) usage() string  {
	return fmt.Sprintf("%.2f",(1 - d.Avail/d.Total)*100) + "%"
}



func (h Host) DiskUsage() (disk []Disk,err error) {
	cfg := api.Config{

		Address: prometheusServer,

	}
	proCli,err := api.NewClient(cfg)
	if err != nil {
		fmt.Println("connect to err is" , err.Error())
		return
	}
	proApi := v1.NewAPI(proCli)
	ctx := context.Background()
	queryList := []string{"node_filesystem_size_bytes","node_filesystem_avail_bytes"}

	for index,query :=range queryList {
		q := fmt.Sprintf("%s{instance=~'%s.*',fstype=~'ext4|xfs'}",query,string(h))

		res,errr := proApi.Query(ctx,q,time.Time{})
		if errr != nil {
			fmt.Println("query error is",errr.Error())
			return
		}
		m,ok := res.(model.Vector)
		if ok {
			for i,p := range m {
				//fmt.Println("第N个分区",i)

				if index == 0 {
					d := Disk{
						Device: string(p.Metric["device"]),
						Fstype: string(p.Metric["fstype"]),
						Mountpoint: string(p.Metric["mountpoint"]),

						Total: float64(p.Value),
					}
					disk = append(disk,d)
				} else if index == 1 {
					disk[i].Avail = float64(p.Value)
					disk[i].Availed = fmt.Sprintf("%.2f",float64(p.Value)/1024/1024/1024) + "G"
					disk[i].Precent = disk[i].usage()
				}

			}
		}
	}


	//根分区使用率
	return
}

type IOSlice struct {
	T []string
	X1,X2 []string
}


func (h Host) DiskIOPS() (ioData IOSlice,err error) {
	cfg := api.Config{
		Address: prometheusServer,
	}
	proCli,err := api.NewClient(cfg)
	if err != nil {
		return
		fmt.Println("connect to err is" , err.Error())
	}
	proApi := v1.NewAPI(proCli)
	ctx := context.Background()
	queryList := []string{"node_disk_reads_completed_total","node_disk_writes_completed_total"}

	for i,query :=range queryList {
		q := fmt.Sprintf("irate(%s{instance=~'%s.*',device=~'sda'}[1m])",query,string(h))

		r := v1.Range{
			End: time.Now(),
			Start: time.Now().Add(-time.Minute * 60),    //一小时内的
			Step: 300 * time.Second,
		}
		res,errr := proApi.QueryRange(ctx,q,r)
		if errr != nil {
			fmt.Println("disk io error is",errr.Error())
			return
		}

		//fmt.Println("disk io data is " , res)
		m,ok := res.(model.Matrix)
		if ok && m.Len() > 0 {
			//fmt.Println("m[0] is ",m[0].Metric)
			//fmt.Println("m[0] is ",m[0].Values)
			v := m[0].Values
			//fmt.Println(len(v))
			//fmt.Println(reflect.TypeOf(m[0].Values))
			for _,samplepair := range v {
				t := samplepair.Timestamp.Time().Format("15:04")
				y,_ := strconv.ParseFloat(samplepair.Value.String(),64)
				ny := fmt.Sprintf("%.2f",y)
				if i == 0 { //receive network io rate
					ioData.T = append(ioData.T , t)
					ioData.X1 = append(ioData.X1,ny)
				} else {
					ioData.X2 = append(ioData.X2,ny)
				}
			}
		}
		//fmt.Println("ok is " , ok)
		//fmt.Println("m is " , m.Len())
	}


	return
}

func (h Host) DiskIORate() (ioData IOSlice,err error) {
	cfg := api.Config{
		Address: prometheusServer,
	}
	proCli,err := api.NewClient(cfg)
	if err != nil {
		fmt.Println("connect to err is" , err.Error())
		return
	}
	proApi := v1.NewAPI(proCli)
	ctx := context.Background()
	queryList := []string{"node_disk_read_bytes_total","node_disk_written_bytes_total"}



	for i,query :=range queryList {
		q := fmt.Sprintf("irate(%s{instance=~'%s.*',device=~'sda'}[1m])",query,string(h))

		r := v1.Range{
			End: time.Now(),
			Start: time.Now().Add(-time.Minute * 60),    //一小时内的
			Step: 300 * time.Second,
		}
		res,errr := proApi.QueryRange(ctx,q,r)
		if errr != nil {
			fmt.Println("disk io error is",errr.Error())
			return
		}

		//fmt.Println("disk io data is " , res)
		m,ok := res.(model.Matrix)
		if ok && m.Len() > 0 {
			//fmt.Println("m[0] is ",m[0].Metric)
			//fmt.Println("m[0] is ",m[0].Values)
			v := m[0].Values
			//fmt.Println(len(v))
			//fmt.Println(reflect.TypeOf(m[0].Values))
			for _,samplepair := range v {
				t := samplepair.Timestamp.Time().Format("15:04")
				y,_ := strconv.ParseFloat(samplepair.Value.String(),64)
				ny := fmt.Sprintf("%.2f",y/1024)
				if i == 0 { //receive network io rate
					ioData.T = append(ioData.T , t)
					ioData.X1 = append(ioData.X1,ny)
				} else {
					ioData.X2 = append(ioData.X2,ny)
				}
			}
		}
		//fmt.Println("ok is " , ok)
		//fmt.Println("m is " , m.Len())
	}
	//fmt.Println("data is " , d)
	//fmt.Println("disk io rate data is " , ioData) //单位 byte/s

	return
}

func (h Host) NetworkIO() (ioData IOSlice,err error) {
	cfg := api.Config{
		Address: prometheusServer,
	}
	proCli,err := api.NewClient(cfg)
	if err != nil {
		fmt.Println("connect to err is" , err.Error())
		return
	}
	proApi := v1.NewAPI(proCli)
	ctx := context.Background()
	queryList := []string{"node_network_receive_bytes_total","node_network_transmit_bytes_total"}

	for i,query :=range queryList {
		//q := fmt.Sprintf("irate(%s{instance=~'%s.*',device=~'ens33.*'}[1m])",query,string(h))
		q := fmt.Sprintf("irate(%s{instance=~'%s.*',device=~'e.*'}[1m])",query,string(h))

		r := v1.Range{
			End: time.Now(),
			Start: time.Now().Add(-time.Minute * 60),    //一小时内的
			Step: 300 * time.Second,
		}
		res,errr := proApi.QueryRange(ctx,q,r)
		if errr != nil {
			fmt.Println("disk io error is",errr.Error())
			return
		}
		//fmt.Println("disk io data is " , res)
		m,ok := res.(model.Matrix)
		if ok && m.Len() > 0 {
			//fmt.Println("m[0] is ",m[0].Metric)

			v := m[0].Values
			fmt.Println(len(v))
			//fmt.Println(reflect.TypeOf(m[0].Values))
			for _,samplepair := range v {
				t := samplepair.Timestamp.Time().Format("15:04")
				y,_ := strconv.ParseFloat(samplepair.Value.String(),64)
				ny := fmt.Sprintf("%.2f",y/1024)
				if i == 0 { //receive network io rate
					ioData.T = append(ioData.T , t)
					ioData.X1 = append(ioData.X1,ny)
				} else {
					ioData.X2 = append(ioData.X2,ny)
				}
			}
		}
		//fmt.Println("ok is " , ok)
		//fmt.Println("m is " , m.Len())
	}


	return
}


//cpu核心数，内存总量
func (h Host) CpuAndMemoryTotal()(core string,memory string,err error)  {
	cfg := api.Config{
		Address: prometheusServer,
	}
	proCli,err := api.NewClient(cfg)
	if err != nil {
		fmt.Println("connect to err is" , err.Error())
		return
	}
	proApi := v1.NewAPI(proCli)
	ctx := context.Background()

	//cpu
	//prometheus query : count(count(node_cpu_seconds_total{instance=~"$node", mode='system'}) by (cpu))
	//q := fmt.Sprintf("%s{instance=~'%s.*'}",query,string(h))
	q := fmt.Sprintf("count(count(node_cpu_seconds_total{instance=~'%s.*', mode='system'}) by (cpu))",string(h))

	fmt.Println("qqqq is reload ",q)

	res,errr := proApi.Query(ctx,q,time.Time{})
	if errr != nil {
		return
		fmt.Println("query error is",errr.Error())
	}
	m,ok := res.(model.Vector)
	if ok && m.Len() > 0 {
		//fmt.Println("thie ip cpu core number is: " , m[0].Value)
		core = m[0].Value.String()
	}
	//内存总量
	q = fmt.Sprintf("node_memory_MemTotal_bytes{instance=~'%s.*'}",string(h))
	res,errr = proApi.Query(ctx,q,time.Time{})
	if errr != nil {
		fmt.Println("query error is",errr.Error())
		return
	}
	m,ok = res.(model.Vector)
	if ok && m.Len() > 0 {
		//fmt.Println("thie ip memory is number is: " , m[0].Value)
		tmp,err := strconv.Atoi(m[0].Value.String())
		if err != nil {
			fmt.Println("内存总量总数错误: ",err.Error())
		}
		if tmp > 1024 * 1024 * 1024 {
			memory = strconv.Itoa(tmp / (1024 * 1024 * 1024)) + "G"
		} else {
			memory = strconv.Itoa(tmp / (1027 * 1024)) + "M"
		}
	}


	return

}

func  urlEncode(u string) (urlparse string) {
	urlParse,_ := url.Parse(u)
	param := urlParse.Query()
	urlParse.RawQuery = param.Encode()
	//	fmt.Println("urlparse is",urlParse)
	return urlParse.String()

}

//根分区使用率
func (h Host)RootUsage() (root string) {
	cfg := api.Config{
		Address: prometheusServer,
	}
	proCli,err := api.NewClient(cfg)
	if err != nil {
		fmt.Println("connect to err is" , err.Error())
		return
	}
	proApi := v1.NewAPI(proCli)
	ctx := context.Background()
	q := fmt.Sprintf("100 - ((node_filesystem_avail_bytes{instance=~'%s.*',mountpoint='/',fstype=~'ext4|xfs'} * 100) / node_filesystem_size_bytes {instance=~'%s.*',mountpoint='/',fstype=~'ext4|xfs'})",string(h),string(h))
	res,errr := proApi.Query(ctx,q,time.Time{})
	if errr != nil {
		fmt.Println("query error is",errr.Error())
		return
	}
	m,ok := res.(model.Vector)
	if ok && m.Len() > 0 {
		//fmt.Println("this root usaged is: " , m[0].Value)
		//core = m[0].Value.String()
		root = fmt.Sprintf("%.2f",float64(m[0].Value))
	}
	return
}

//当前打开的文件描述符
func (h Host)FileDescribe()(f float64)  {
	cfg := api.Config{
		Address: prometheusServer,
	}
	proCli,err := api.NewClient(cfg)
	if err != nil {
		fmt.Println("connect to err is" , err.Error())
		return
	}
	proApi := v1.NewAPI(proCli)
	ctx := context.Background()

	//cpu
	//prometheus query : count(count(node_cpu_seconds_total{instance=~"$node", mode='system'}) by (cpu))
	//q := fmt.Sprintf("%s{instance=~'%s.*'}",query,string(h))
	q := fmt.Sprintf("node_filefd_allocated{instance=~'%s.*'}",string(h))
	res,errr := proApi.Query(ctx,q,time.Time{})
	if errr != nil {
		fmt.Println("query error is",errr.Error())
		return
	}
	m,ok := res.(model.Vector)
	if ok && m.Len() > 0 {
		//fmt.Println("this file describe opend : " , m[0].Value)
		//core = m[0].Value.String()
		f = float64(m[0].Value/1000)
	}
	return

}


func (h Host)TcpStat() (tcpstatus []int)  {
	cfg := api.Config{
		Address: prometheusServer,
	}
	proCli,err := api.NewClient(cfg)
	if err != nil {
		fmt.Println("connect to err is" , err.Error())
		return
	}
	proApi := v1.NewAPI(proCli)
	ctx := context.Background()
	queryList := []string{"node_netstat_Tcp_CurrEstab","node_sockstat_TCP_tw",}

	for _,query :=range queryList {
		q := fmt.Sprintf("%s{instance=~'%s.*'}",query,string(h))
		res,errr := proApi.Query(ctx,q,time.Time{})
		if errr != nil {
			fmt.Println("query error is",errr.Error())
			return
		}
		m,ok := res.(model.Vector)
		if ok && m.Len() > 0 {
			//d = append(d,float64(m[0].Value))

			tcpstatus = append(tcpstatus,int(m[0].Value))
		}
	}
	return

}

//TCP连接情况

type TcpMertic struct {
	T      []string
	Estab,TW,ActiveOpens,PassiveOpens,Alloc,Inuse []int64
}

func (h Host) TcpConnStat() (ioData TcpMertic,err error) {
	cfg := api.Config{
		Address: prometheusServer,
	}
	proCli,err := api.NewClient(cfg)
	if err != nil {
		fmt.Println("connect to err is" , err.Error())
		return
	}
	proApi := v1.NewAPI(proCli)
	ctx := context.Background()
	queryList := []string{"node_netstat_Tcp_CurrEstab","node_sockstat_TCP_tw","node_netstat_Tcp_ActiveOpens","node_netstat_Tcp_PassiveOpens","node_sockstat_TCP_alloc","node_sockstat_TCP_inuse"}

	for i,query :=range queryList {
		//q := fmt.Sprintf("irate(%s{instance=~'%s.*'}[1m])",query,string(h))
		q := fmt.Sprintf("%s{instance=~'%s.*'}",query,string(h))

		r := v1.Range{
			End: time.Now(),
			Start: time.Now().Add(-time.Minute * 60),    //一小时内的
			Step: 300 * time.Second,
		}
		res,errr := proApi.QueryRange(ctx,q,r)
		if errr != nil {
			fmt.Println("disk io error is",errr.Error())
			return
		}
		//fmt.Println("disk io data is " , res)
		m,ok := res.(model.Matrix)
		if ok && m.Len() > 0 {
			//fmt.Println("m[0] is ",m[0].Metric)
			//fmt.Println("m[0] is ",m[0].Values)
			v := m[0].Values

			//fmt.Println(reflect.TypeOf(m[0].Values))
			for _,samplepair := range v {
				t := samplepair.Timestamp.Time().Format("15:04")
				//y,_ := strconv.ParseFloat(samplepair.Value.String(),64)
				ny := int64(samplepair.Value)
				switch i {
				//Estab,TW,ActiveOpens,PassiveOpens,Alloc,Inuse []string
				case 0 :
					ioData.T = append(ioData.T , t)
					ioData.Estab = append(ioData.Estab,ny)
				case 1:
					ioData.TW = append(ioData.TW,ny)
				case 2:
					ioData.ActiveOpens = append(ioData.ActiveOpens,ny)
				case 3:
					ioData.PassiveOpens = append(ioData.PassiveOpens,ny)
				case 4:
					ioData.Alloc = append(ioData.Alloc,ny)
				case 5:
					ioData.Inuse = append(ioData.Inuse,ny)
				}
			}
		}
		//fmt.Println("ok is " , ok)
		//fmt.Println("m is " , m.Len())
	}
	//fmt.Println("TCP connect stat is  ," , ioData)

	return
}